/*
 * Exploder
 * 
 * Tero Pyylampi (218437), Jussi Tuurinkoski (211594)
 *
 * Steel bar element for the game
 */

#include "GameElementSteelBar.h"
#include "../../../GameCamera.h"
#include "../../../effects/GameEffects.h"
#include "../../../../Common.h"
#include "../../../../Resources.h"

#include "Box2D/Box2D.h"
#include <math.h>
#include <vector>

using namespace cocos2d;

// default constructor
GameElementSteelBar::GameElementSteelBar( b2World *world, cocos2d::CCLayer *layer, GameCamera *camera, bool editingMode, b2Vec2 worldPos, ElementType properties, float rotation )
	: GameConstructionElement( world, layer, camera, editingMode, properties ), mInitialSpriteAngle( 0 )
{
	mInitialPosition.Set( worldPos.x, worldPos.y );

	// initialize element
	if( editingMode ) 
		initObjectInEditingMode( worldPos );
	else
		initObjectInGameMode( worldPos, rotation );
}


// default destructor
GameElementSteelBar::~GameElementSteelBar()
{
}


// update function
void GameElementSteelBar::update( cocos2d::ccTime dt ) {
	GameConstructionElement::update( dt );
}

// apply explosion force, the object itself should decide whether to break or not
void GameElementSteelBar::applyExplosionToElements( b2Vec2 sourcePos, float force ) {
	GameConstructionElement::applyExplosionToElements( sourcePos, force );
}

// change object sprite to broken
void GameElementSteelBar::changeSpriteToBroken() {
	// TODO
}

// break object into pieces
void GameElementSteelBar::breakObject() {
	// save old position
	b2Vec2 currentPos = mBodies->at( 0 )->GetPosition();
	CCPoint screenPos = mGameCamera->convertWorldToScreen( currentPos.x, currentPos.y );
	float32 currentAngle = mBodies->at( 0 )->GetAngle();

	// destroy old physics body
	mGameWorld->DestroyBody( mBodies->at( 0 ) );
	mBodies->clear();

	// remove sprite from layer
	mLayer->removeChild( mIntactSprite, true );
	mIntactSprite = 0;
	
	// calculate positions and create new bodies
	ObjectProperties properties( 1.0f, 0.7f, 0.2f );
	b2Vec2 newPosition; 
	newPosition.SetZero();

	// save initial angle
	//mInitialSpriteAngle = CC_RADIANS_TO_DEGREES( currentAngle );

	newPosition = calculatePosition( currentPos, currentAngle, 0 );
	b2Body *b1 = this->createBox( false, 2.0f, 2.0f, newPosition.x, newPosition.y, properties );
	newPosition = calculatePosition( currentPos, currentAngle, 1 );
	b2Body *b2 = this->createBox( false, 2.0f, 2.0f, newPosition.x, newPosition.y, properties );
	newPosition = calculatePosition( currentPos, currentAngle, 2 );
	b2Body *b3 = this->createBox( false, 2.0f, 2.0f, newPosition.x, newPosition.y, properties );
	newPosition = calculatePosition( currentPos, currentAngle, 3 );
	b2Body *b4 = this->createBox( false, 2.0f, 2.0f, newPosition.x, newPosition.y, properties );
	mBodies->push_back( b1 );
	mBodies->push_back( b2 );
	mBodies->push_back( b3 );
	mBodies->push_back( b4 );

	// rotate object
	for( unsigned int i = 0; i < mBodies->size(); ++i )
		mBodies->at( i )->SetTransform( mBodies->at( i )->GetPosition(), currentAngle );
	
	// insert sprites into layer
	for( unsigned int i = 0; i < 4; ++i ) {
		mLayer->addChild( mBrokenSprites[ i ], Z_LAYER_GAME_MIDDLE );
		mBrokenSprites[ i ]->release();
	}

	// match the positions
	update( 0 );

	// show effect
	// TODO: looks like a crap
	//cocos2d::CCParticleSystem *effect = GameEffects::createBreakingParticles( screenPos.x, screenPos.y, getResource( Textures::GAME_MISC_SPLINTER ), 15 );
	//mLayer->addChild( effect, Z_LAYER_GAME_FRONT );
}

// get the main type of the object
PhysicObjectItems GameElementSteelBar::getObjectType() {
	return ELEMENT_STEEL_BAR;
}

// init this object in editing mode
void GameElementSteelBar::initObjectInEditingMode( b2Vec2 worldPos ) {
	// create physics object
	ObjectProperties properties( 1.0f, 0.7f, 0.2f );
	b2Body *barBody = createBox( true, 8.0f, 2.0f, worldPos.x, worldPos.y, properties );
	mBodies->push_back( barBody );

	// create sprites
	mIntactSprite = CCSprite::spriteWithFile( getResource( Textures::GAME_ELEMENT_STEEL_BAR ) );
	mIntactSprite->setScale( RATEX * 0.32f );
	mLayer->addChild( mIntactSprite, Z_LAYER_GAME_MIDDLE );

	// init variables
	mCurrentlyActive = false;
}

// init this object in game mode
void GameElementSteelBar::initObjectInGameMode( b2Vec2 worldPos, float rotation ) {
	// create physics object
	ObjectProperties properties( 1.0f, 0.7f, 0.2f );
	b2Body *barBody = createBox( false, 8.0f, 2.0f, worldPos.x, worldPos.y, properties );
	barBody->SetTransform( barBody->GetPosition(), rotation );
	mBodies->push_back( barBody );

	// create normal sprite
	mIntactSprite = CCSprite::spriteWithFile( getResource( Textures::GAME_ELEMENT_STEEL_BAR ) );
	mIntactSprite->setScale( RATEX * 0.322f );
	mLayer->addChild( mIntactSprite, Z_LAYER_GAME_MIDDLE );

	// create broken sprites
	mBrokenSprites[ 0 ] = CCSprite::spriteWithFile( getResource( Textures::GAME_ELEMENT_STEEL_BAR_PART_A ) );
	mBrokenSprites[ 1 ] = CCSprite::spriteWithFile( getResource( Textures::GAME_ELEMENT_STEEL_BAR_PART_B ) );
	mBrokenSprites[ 2 ] = CCSprite::spriteWithFile( getResource( Textures::GAME_ELEMENT_STEEL_BAR_PART_B ) );
	mBrokenSprites[ 3 ] = CCSprite::spriteWithFile( getResource( Textures::GAME_ELEMENT_STEEL_BAR_PART_C ) ); 
	for( unsigned int i = 0; i < 4; ++i ) {
		mBrokenSprites[ i ]->setScale( RATEX * 0.322f );
		mBrokenSprites[ i ]->retain();
	}

	// init variables
	mObjectMaxDurability = 500.0f;
	mObjectDurabilityLeft = mObjectMaxDurability;

	if( mElementType == SHOULD_NOT_MOVE )
		setRedSpriteColors();
}

// calculates positions for new bodies which are created when steel bar breaks into pieces
b2Vec2 GameElementSteelBar::calculatePosition( b2Vec2 position, float32 angle, int part )
{
	// init temporary variable
	b2Vec2 tmpPos;
	tmpPos.SetZero();
	float32 tmpX = 0.0f;
	float32 tmpY = 0.0f;
	const double PI = 3.141592653589793238462;
	float tangent = 0.0f;

	// determine the length of tangent
	if( part == 1 || part == 2 )
	{
		tangent = 1.0f;
	}
	else
	{
		tangent = 3.0f;
	}

	// needed conversions for angle
	if( angle < 0 )
	{
		angle = angle * ( -1.0f );
	}
	int numberOfRotations = angle / ( 2 * PI );
	angle = ( 2 * PI ) - ( angle - ( numberOfRotations * 2 * PI ) );

	// calculate the difference from the center
	tmpX = cos( angle ) * tangent;
	tmpY = sin( angle ) * tangent;

	// for two first bodies from left
	if( part == 0 || part == 1 )
	{
		tmpX = tmpX * ( -1 );
		tmpY = tmpY * ( -1 );
	}

	// calculate position for new body
	tmpX = position.x + tmpX;
	tmpY = position.y + tmpY;
	tmpPos.Set( tmpX, tmpY );

	// return the calculated position for new body
	return tmpPos;
}